package models

//humans.go datatype of the people in the photo. Not called students as there
// are other people in the photo (eg master). Not calling them subjects as this
// could get confusing because the students studied subjects at College.

var humanStore HumanStorer

type HumanError struct {
	errorText string
}

func NewHumanError(errorMessage string) error {
	return HumanError{errorMessage}
}

func (h HumanError) Error() string {
	return "HumanError: " + h.errorText
}

//Should be used at the beginning of methods which make user of the UserStorer. There is no point trying to
//recover in this situation.
func panicIfUnsetHumanStorer() {
	if humanStore == nil {
		panic("Cannot Save user with no HumanStorer set. HumanStorer can be set by calling SetHumanStorer.")
	}
}

type Human struct {
	Id         int64
	Name       string
	IsStudent  bool
	TwitterId  string
	LinkedinId string
	FacebookId string
	UserId     int64
	GeoCoord   geoCoord
	PhotoCoord photoCoord
	Subject    string
	ClassId    int64
}

type geoCoord struct {
	Latitude  float64
	Longitude float64
}

type photoCoord struct {
	X uint32
	Y uint32
}

func NewHuman(
	name string,
	isStudent bool,
	twitterId string,
	linkedinId string,
	facebookId string,
	longitude float64,
	latitude float64,
	photoX uint32,
	photoY uint32,
	subject string,
	classId int64) (*Human, error) {
	if latitude < -90 || latitude > 90 {
		return nil, NewHumanError("Latitude must be between -90 and +90.")
	}
	if longitude < -180 || longitude > 180 {
		return nil, NewHumanError("Longitude must be between -180 and +180.")
	}
	return &Human{
		Name:       name,
		IsStudent:  isStudent,
		TwitterId:  twitterId,
		LinkedinId: linkedinId,
		FacebookId: facebookId,
		Subject:    subject,
		GeoCoord: geoCoord{
			Latitude:  latitude,
			Longitude: longitude},
		PhotoCoord: photoCoord{
			X: photoX,
			Y: photoY},
		ClassId: classId}, nil
}

func (h *Human) SaveNew() error {
	panicIfUnsetHumanStorer()
	return humanStore.SaveNew(h)
}

func (h *Human) Update() error {
	panicIfUnsetHumanStorer()
	return humanStore.Update(h)
}

func (h *Human) SetUserId(userId int64) error {
	panicIfUnsetHumanStorer()
	if h.Id == 0 {
		return NewHumanError("Cannot set user id before human has been saved, call human.SaveNew() first.")
	}
	err := humanStore.SetUserId(h.Id, userId)
	if err == nil {
		h.UserId = userId
	}
	return err
}

func (h *Human) Delete() error {
	panicIfUnsetHumanStorer()
	if h.Id == 0 {
		return NewHumanError("User not saved, cannot delete.")
	}
	return humanStore.Delete(h.Id)
}

func GetAllHumans() ([]Human, error) {
	panicIfUnsetHumanStorer()
	return humanStore.GetAll()
}

func GetHumansToBlur() ([]Human, error) {
	panicIfUnsetHumanStorer()
	return humanStore.GetHumansToBlur()
}

func GetUnblurredHumans() ([]Human, error) {
	panicIfUnsetHumanStorer()
	return humanStore.GetUnblurred()
}

func GetHumanByUserId(userId int64) (*Human, error) {
	panicIfUnsetHumanStorer()
	return humanStore.GetByUserId(userId)
}

type HumanStorer interface {
	SaveNew(*Human) error //Make sure to update human with Id!
	Update(*Human) error
	SetUserId(int64, int64) error
	Delete(int64) error
	GetByUserId(int64) (*Human, error)
	GetAll() ([]Human, error)
	GetHumansToBlur() ([]Human, error)
	GetUnblurred() ([]Human, error)
}

func SetHumanStore(hs HumanStorer) {
	humanStore = hs
}
